#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>//printk()
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>//u8,u16,u3……
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/sched.h>//和任务相关
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>//copy_to_user(),copy_from_user()
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/io.h>


/*********************************************************************/
#define SPI_NAME "spi"
static int SPI_MAJOR = 55;
struct spi_dev
{
	struct cdev cdev;
	char dataTx;
	char dataRx;
};

struct spi_dev *spi_devp;


/************************************************************************/
//GPG and GPE control register setting


#define GPE_DEFAULT ((S3C2410_GPE11_SPIMISO0|S3C2410_GPE12_SPIMOSI0|S3C2410_GPE13_SPICLK0))

#define SPI_CON_DEFAULT (0<<0|0<<1|0<<2|1<<3|1<<4|0<<5|0<<6)//具体设置根据情况确定

#define SPI_TXRX_READY ((readb(SPSTA0)&0x01) == 0x1) 
#define SPI_TXRX_BUSY  ((readb(SPSTA0)&0x01) == 0x0)
#define SPI_GPE_STATUS ((readw(GPEDAT)&0x0800)== 0x0000)


/************************************************************************/

//GPG GPE SPI address declare
//GPG
unsigned long *GPGCON;
unsigned long *GPGDAT;
unsigned long *GPGUP;
//GPE
unsigned long *GPECON;
unsigned long *GPEDAT;
unsigned long *GPEUP;
//SPI
unsigned long *SPCON0;
unsigned long *SPSTA0;
unsigned long *SPPIN0;
unsigned long *SPPRE0;
unsigned long *SPTDAT0;
unsigned long *SPRDAT0;
//CLK control
unsigned long *CLKCON;

/***************************************************************************/

/*** address ioremap ***/
void spi_ioremap(void)
{
//GPG GPE SPI address declare
	//GPG
	GPGCON = ioremap(0x56000060,4);
	GPGDAT = ioremap(0x56000064,4);
	GPGUP  = ioremap(0x56000068,4);
	//GPE
	GPECON = ioremap(0x56000040,4);
	GPEDAT = ioremap(0x56000044,4);
	GPEUP  = ioremap(0x56000048,4);
	//SPI
	SPCON0 = ioremap(0x59000000,4);
	SPSTA0 = ioremap(0x59000004,4);
	SPPIN0 = ioremap(0x59000008,4);
	SPPRE0 = ioremap(0x5900000c,4);
	SPTDAT0 = ioremap(0x59000010,1);
	SPRDAT0 = ioremap(0x59000014,1);
	//CLK control
	CLKCON = ioremap(0x4c00000c,4);	

	return ;	
}

/***取消地址映射 ***/
void spi_cancle_ioremap(void)
{
	iounmap(GPGCON);
	iounmap(GPGDAT);
	iounmap(GPGUP);

	iounmap(GPECON);
	iounmap(GPEDAT);
	iounmap(GPEUP);

	iounmap(SPCON0);
	iounmap(SPSTA0);
	iounmap(SPPIN0);
	iounmap(SPPRE0);
	iounmap(SPTDAT0);
	iounmap(SPRDAT0);

  	iounmap(CLKCON);

	return ;

}

/********************************************************/
//wzk2440 SPI config
static void wzk2440_spi_config(void)
{
	unsigned char temp;
	unsigned int port_status;
	unsigned int i = 0;
	unsigned char tem = 0xFF;
	
	
	port_status = readl(CLKCON);
	port_status |= (1<<18);
	writel(port_status,CLKCON);	//时钟使能
	
	port_status = readl(GPGCON);
	port_status &= ~(1<<4 | 1<<5);
	port_status |= (1<<4 | 0<<5);	//GPG control
	writel(port_status,GPGCON);
	
	port_status=readl(GPECON);
 	port_status&=~GPE_DEFAULT;
 	port_status|=GPE_DEFAULT;
 	writel(port_status,GPECON);//GPE control

	port_status = 0x0;
	port_status = readl(GPEUP);
	port_status |= (1<<13 | 1<<12 | 1<<11);
	writel(port_status,GPEUP);	//GPE_UP
	
	port_status = 0x0;
	port_status = readl(GPGUP);
	port_status |= (1<<2 | 1<<3 | 1<<6);
	writel(port_status,GPGUP); 	//GPG_UP

	port_status = readl(SPPRE0);	//SPI Baud speed
	port_status = 0x18;
	writel(port_status,SPPRE0); 
	temp = readb(SPPRE0);
	printk("sppre0 = %2x \n",temp);

	port_status = readl(SPCON0);
	port_status = SPI_CON_DEFAULT;
	writel(port_status,SPCON0);	//SPI control
	temp = readb(SPCON0);
	printk("spcon0 = %2x \n",temp);

	port_status = readl(SPPIN0);
	port_status &= ~(1<<0 | 1<<1 | 1<<2);
	port_status |= (1<<0 | 0<<1 | 0<<2);
	writel(port_status,SPPIN0);	//SPI pin setting
	temp = readb(SPPIN0);
	printk("sppin0 = %2x \n",temp);

	for(i = 0; i < 10; i++)
	{
		writeb(tem,SPTDAT0);
	}
	
		
}

/***驱动保持 ***/
static void spi_spin(void)
{
	unsigned char temp;
	unsigned int port_status;
	
	port_status = readl(SPPIN0);
        port_status &= ~(1<<0 | 1<<1 | 1<<2);
        writel(port_status,SPPIN0);     //SPI pin setting
        temp = readb(SPPIN0);

}

/********************************************************************/
static int spi_open(struct inode *inode,struct file *filp)
{
	filp->private_data = spi_devp;
	wzk2440_spi_config();
	s3c2410_gpio_setpin(S3C2410_GPG2,0);	//nSS0低电平
	printk("-----------------------------\n");
	printk("wzk2440 SPI GPG GPE CLK set over\n");
	printk("-----------------------------\n");

	return 0;
}

static int spi_release(struct inode *inode,struct file *filp)
{
	printk("-----------------------------\n");
        printk("wzk2440 SPI GPG GPE CLK set over\n");
        printk("-----------------------------\n");

	return 0;
}


/***********************************************************************/

static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
	int ret;
	unsigned char temp = 0xFF;

	struct spi_dev *dev = filp->private_data;
	while(!SPI_TXRX_READY)
	{
//		printk("rx unready!\n");
	}
	writeb(temp,SPTDAT0);
	while(!SPI_TXRX_READY)
	{
//		printk("rx unready!\n");
	}
	udelay(10);
	spi_spin();
	dev->dataRx = readb(SPRDAT0);
	
	ret = copy_to_user(buf,&(dev->dataRx),count);
	if(ret < 0)
	{
		printk("copy_to_user error!\n");
		return -1;
	}
	return 0;
}

static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops)
{
	volatile int endSpiTx = 0;
	volatile char spiTxData;
	int ret;
	struct spi_dev *dev=filp->private_data;
	
	ret = copy_from_user(&(dev->dataTx),buf,count);
	if(ret < 0)
	{
		printk("copy_from_user error!\n");
		return -1;	
	}
	
	
	spiTxData = dev->dataTx;
	
	while(endSpiTx == 0)
	{

		while(!SPI_TXRX_READY)
		{	
//			printk("SPI TX unready \n");
		}
		
		writeb(spiTxData,SPTDAT0);
		udelay(10);
	
	
		endSpiTx = 1;
	}

	return 0;

}


/******spi ioctl ************/
static int spi_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg)
{
	
	return 0;	
}

//-------------------------------------------------------------
static const struct file_operations spi_fops = {

	.owner = THIS_MODULE,
	.open = spi_open,
	.release = spi_release,
	.read = spi_read,
	.write = spi_write,
	.ioctl = spi_ioctl,
};

static void spi_setup_cdev(struct spi_dev *dev,int index)
{
	int err;
	int devno = MKDEV(SPI_MAJOR,index);
	cdev_init(&dev->cdev,&spi_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &spi_fops;
	err = cdev_add(&dev->cdev,devno,1);
	if(err)
		printk("error\n");
	printk("------------------------------\n");
	
}
/****建立设备节点的类 ***/
static struct class *spi_class;


static int __init spi_init(void)
{
	int ret;
	dev_t devno = MKDEV(SPI_MAJOR,0);
	
	ret = register_chrdev_region(devno,1,SPI_NAME);
	spi_devp = kmalloc(sizeof(struct spi_dev),GFP_KERNEL);
	if(!spi_devp)
	{
		ret = -ENOMEM;
		goto fail_malloc;
	}
	memset(spi_devp,0,sizeof(struct spi_dev));
	
	spi_setup_cdev(spi_devp,0);
	
	/***设备节点的建立***/
	spi_class = class_create(THIS_MODULE,SPI_NAME);
	if(IS_ERR(spi_class))
	{
		printk("Err:failed in wzk_spi class.\n");
	}
	device_create(spi_class,NULL,MKDEV(SPI_MAJOR,0),NULL,SPI_NAME);

	printk("init spi success\n");

	//地址映射
	spi_ioremap();

	return 0;

fail_malloc:
	unregister_chrdev_region(devno,1);

	return ret;
}

static void __exit spi_exit(void)
{
	//free_irq(IRQ_SPI0,NULL);

	//cancle address ioremap

	spi_cancle_ioremap();

	unregister_chrdev(SPI_MAJOR,SPI_NAME);
	device_destroy(spi_class,MKDEV(SPI_MAJOR,0));
}

//---------------------------------------------------
module_init(spi_init);
module_exit(spi_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("WZK");
MODULE_DESCRIPTION("SPI DRIVER FOR WZK_S3C2440");
